/*
 * Funny Language - a free style programming language.
 * Copyright (C) 2015 by fanguangping (fanguangping@163.com)
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include "runner_translate.h"

// {define function {add}: {add {a} {b}} as {{a} + {b}}}
static Statement *create_statement() {
    Statement *s = (Statement *)malloc(sizeof(Statement));
    Statement *arg1 = NULL;
    Statement *arg2 = NULL;
    Statement *arg3 = NULL;
    Statement *arg4 = NULL;
    Statement *arg5 = NULL;
    Statement *arg6 = NULL;
    Statement *arg7 = NULL;
    s->symbol = NULL;
    s->type = ARG;
    s->children = init_queue();
    add_statement(s, "define", SYM);
    add_statement(s, "function", SYM);
    arg1 = add_statement(s, NULL, ARG);
    add_statement(arg1, "add", SYM);
    add_statement(s, ":", SYM);
    arg2 = add_statement(s, NULL, ARG);
    add_statement(arg2, "add", SYM);
    arg3 = add_statement(arg2, NULL, ARG);
    add_statement(arg3, "a", SYM);
    arg4 = add_statement(arg2, NULL, ARG);
    add_statement(arg4, "b", SYM);
    add_statement(s, "as", SYM);
    arg5 = add_statement(s, NULL, ARG);
    arg6 = add_statement(arg5, NULL, ARG);
    add_statement(arg6, "a", SYM);
    add_statement(arg5, "+", SYM);
    arg7 = add_statement(arg5, NULL, ARG);
    add_statement(arg7, "b", SYM);
    return s;
}

// {case [when {a} then {b} ,] else {c}}
static Statement *create_loop_statement() {
    Statement *s = (Statement *)malloc(sizeof(Statement));
    Statement *arg1 = NULL;
    Statement *arg2 = NULL;
    Statement *arg3 = NULL;
    Statement *arg4 = NULL;
    s->symbol = NULL;
    s->type = ARG;
    s->children = init_queue();
    add_statement(s, "case", SYM);
    arg1 = add_statement(s, NULL, LOOP);
    add_statement(arg1, "when", SYM);
    arg2 = add_statement(arg1, NULL, ARG);
    add_statement(arg2, "a", SYM);
    add_statement(arg1, "then", SYM);
    arg3 = add_statement(arg1, NULL, ARG);
    add_statement(arg3, "b", SYM);
    // add_statement(arg1, ",", SYM);  //comma is ignored
    add_statement(s, "else", SYM);
    arg4 = add_statement(s, NULL, ARG);
    add_statement(arg4, "c", SYM);
    return s;
}

static void test_next_statement() {
    String statement_text = init_string();
    statement_text = dyna_strcat(statement_text, STATEMENT);
    statement_text = dyna_strcat(statement_text, "\n");
    statement_text = dyna_strcat(statement_text, LOOP_STATEMENT);
    statement_text = dyna_strcat(statement_text, "\n");
    FILE *file = create_temp_file(statement_text);
    rewind(file);
    int hasNext = has_next_statement(file);
    assert_true(hasNext);
    Statement *s = next_statement(file);
    assert_true(equal_statement(s, create_statement()));
    hasNext = has_next_statement(file);
    assert_true(hasNext);
    s = next_statement(file);
    assert_true(equal_statement(s, create_loop_statement()));
    hasNext = has_next_statement(file);
    assert_false(hasNext);
}

static void test_read_statement() {
    String statement_text = init_string();
    statement_text = dyna_strcat(statement_text, STATEMENT);
    statement_text = dyna_strcat(statement_text, "\n");
    statement_text = dyna_strcat(statement_text, LOOP_STATEMENT);
    statement_text = dyna_strcat(statement_text, "\n");
    FILE *file = create_temp_file(statement_text);
    rewind(file);
    Statement *s = read_statement(file);
    assert_true(equal_statement(s, create_statement()));
    s = read_statement(file);
    assert_true(equal_statement(s, create_loop_statement()));
    fclose(file);
}

static void test_parse_statement() {
    Statement* statement = parse(STATEMENT);
    assert_true(equal_statement(statement, create_statement()));
}

static void test_parse_loop_statement() {
    Statement* loop_statement = parse(LOOP_STATEMENT);
    assert_true(equal_statement(loop_statement, create_loop_statement()));
}


int main(int argc, char **argv) {
    const struct CMUnitTest tests[] = {
            cmocka_unit_test(test_next_statement),
            cmocka_unit_test(test_read_statement),
            cmocka_unit_test(test_parse_statement),
            cmocka_unit_test(test_parse_loop_statement),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}
